package com.ruoyi.web.common.util;

import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.utils.format.TimeUtils;
import com.ruoyi.common.utils.msg.codec.FrameBodyDecoderImpl;
import com.ruoyi.common.utils.msg.codec.FrameEncoder;
import com.ruoyi.common.utils.msg.codec.FrameHeaderDecoderImpl;
import com.ruoyi.common.utils.msg.codec.NettyFrameDecoder;
import com.ruoyi.common.utils.msg.dto.req.FrameReqWrapper;
import com.ruoyi.common.utils.msg.util.format.Body;
import com.ruoyi.common.utils.msg.util.format.ReservoirCapacityCalculator;
import com.ruoyi.common.utils.msg.util.format.TransferFormatUtil;
import com.ruoyi.common.utils.msg.util.format.WindDirectionUtil;
import com.ruoyi.iot.domain.IotDataWaterPressureInfo;
import com.ruoyi.iot.domain.IotFuncDict;
import com.ruoyi.iot.domain.IotSensorMsg;
import com.ruoyi.iot.service.IIotFuncDictService;
import com.ruoyi.iot.service.IIotSensorCollectService;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 水利报文解析工具类
 * @Author: ming
 * @Date: 2022/03/09/10:05
 */
@Service
public class MsgParsingUtil {

    @Resource
    private IIotSensorCollectService iotService;

    @Resource
    private IIotFuncDictService dictService;

    @Resource
    private FrameEncoder frameEncoder;

    private static final List<String>  SYJ_TAG = Arrays.asList(
            "ff14", "ff15", "ff16", "ff17", "ff18", "ff19", "ff20", "ff21",
            "ff22", "ff23", "ff24", "ff25", "ff26", "ff27", "ff28", "ff29"
    );

    public String parseMsg(String msg) throws DecoderException {
        ByteBuf buffer = Unpooled.copiedBuffer(Hex.decodeHex(msg));
        NettyFrameDecoder decoder =
                new NettyFrameDecoder(false, new FrameHeaderDecoderImpl(), new FrameBodyDecoderImpl());
        FrameReqWrapper wrapper = decoder.decodeFrame(buffer);

        Map<String, Object> formatData = TransferFormatUtil.getFormatData(wrapper);
        Map<String, Object> data = (Map<String, Object>) formatData.get("data");
        dealWithSensorData(data);

        return null;
    }

    /**
     * 解析渗压数据报文
     * @param msg
     * @return
     * @throws DecoderException
     */
    public String parseSYMsg(String msg) throws DecoderException {
        ByteBuf buffer = Unpooled.copiedBuffer(Hex.decodeHex(msg));
        NettyFrameDecoder decoder =
                new NettyFrameDecoder(false, new FrameHeaderDecoderImpl(), new FrameBodyDecoderImpl());
        FrameReqWrapper wrapper = decoder.decodeFrame(buffer);

        Map<String, Object> formatData = TransferFormatUtil.getFormatData(wrapper);
        Map<String, Object> data = (Map<String, Object>) formatData.get("data");
        dealWithMCUSensorData(data);

        return null;
    }

    private void dealWithMCUSensorData(Map<String, Object> data) {
        if (data == null) {
            return; // 或者初始化为空 map：data = Collections.emptyMap();
        }
        // 用于按 deviceId 分组存储 fields 和 tags
        Map<String, Map<String, Object>> deviceFieldsMap = new HashMap<>();
        Map<String, Map<String, String>> deviceTagsMap = new HashMap<>();
        Map<String, Map<String, Object>> mysqlDeviceFieldsMap = new HashMap<>();
        Map<String, Map<String, String>> mysqlDeviceTagsMap = new HashMap<>();
        //存储表名称
        Map<String, String> tableNameMap = new HashMap<>();

        //获取所有渗压计原始数据
        Map<String, IotDataWaterPressureInfo> allPressureInfo = iotService.getAllPressureInfo();

        data.forEach((key, body) -> {
            Body frameData = (Body) body;
            IotFuncDict funcDictMap = dictService.getMeasurementDictMap(frameData.getFuncCode());
            String deviceId =iotService.geSensorIdByRemoteIdANDFuncCode(frameData.getRemoteId(),frameData.getFuncCode());

            if(ObjectUtil.isEmpty(funcDictMap) || ObjectUtil.isEmpty(deviceId) || !SYJ_TAG.contains(key)) {
                return ;
            }


            String remoteId = frameData.getRemoteId();

            // 初始化或获取该设备的 fields 和 tags
            deviceFieldsMap.putIfAbsent(deviceId, new HashMap<>());
            deviceTagsMap.putIfAbsent(deviceId, new HashMap<>());
            mysqlDeviceFieldsMap.putIfAbsent(deviceId, new HashMap<>());
            mysqlDeviceTagsMap.putIfAbsent(deviceId, new HashMap<>());

            Map<String, Object> fields = deviceFieldsMap.get(deviceId);
            Map<String, String> tags = deviceTagsMap.get(deviceId);
            Map<String, Object> mysqlFields = mysqlDeviceFieldsMap.get(deviceId);
            Map<String, String> mysqlTags = mysqlDeviceTagsMap.get(deviceId);

            // 设置 tags（只需设置一次）
            if (!tags.containsKey("deviceId")) {
                //插入表名称
                tableNameMap.put(deviceId+"mysql", funcDictMap.getTableName());
                tableNameMap.put(deviceId+"influxdb", funcDictMap.getMeasurement());
                tags.put("deviceId", deviceId);
                tags.put("remoteId", remoteId);
                mysqlTags.put("device_id", deviceId);
                mysqlTags.put("remote_id", remoteId);

//                String reportTime = TimeUtils.formatStandard(frameData.getReportTime()); // 转换为 "yyyy-MM-dd HH:mm:ss"
                String reportTime = TimeUtils.formatStandardWithOneHourAdded(frameData.getReportTime());
                fields.put("reportTime", reportTime);
                fields.put("sendTime", frameData.getSendTime());
                fields.put("reportText", frameData.getOriginalCode());
//                fields.put("funcCode", frameData.getFuncCode());
                mysqlFields.put("report_time", reportTime);
                mysqlFields.put("update_time", frameData.getSendTime());
                mysqlFields.put("report_text", frameData.getOriginalCode());
//                mysqlFields.put("func_code", frameData.getFuncCode());
            }
            //判断原始值是否为空  为空说明渗压孔为干孔   则该水位高程为渗压计高程
            String height = allPressureInfo.get(deviceId).getPiezometerHeight();
            if (ObjectUtil.isEmpty(frameData.getFormatValue())){
                fields.put(funcDictMap.getFieldName(), height);
                mysqlFields.put(funcDictMap.getMysqlFieldName(), height);
            }else {
                String value = addStringDoubles(height, frameData.getFormatValue());
                fields.put(funcDictMap.getFieldName(), value);
                mysqlFields.put(funcDictMap.getMysqlFieldName(), value);
            }

        });
        // 遍历分组后的数据，统一写入 InfluxDB / MySQL
        deviceFieldsMap.forEach((item,dataFields) -> {
            Map<String, Object> mysqlDataFields = mysqlDeviceFieldsMap.get(item);
            Map<String, String> tagParams = deviceTagsMap.get(item);
            iotService.saveToInfluxDB(tableNameMap.get(item+"influxdb"), tagParams, dataFields);
            boolean sameHour = TimeUtils.isSameHour((String) mysqlDataFields.get("update_time"), (String) mysqlDataFields.get("report_time"));
            if (sameHour){
                iotService.saveToMysql(tableNameMap.get(item+"mysql"), mysqlDeviceTagsMap.get(item), mysqlDataFields);
            }
        });
    }


    private void dealWithSensorData(Map<String, Object> data) {
        if (data == null) {
            return; // 或者初始化为空 map：data = Collections.emptyMap();
        }
        // 用于按 deviceId 分组存储 fields 和 tags
        Map<String, Map<String, Object>> deviceFieldsMap = new HashMap<>();
        Map<String, Map<String, String>> deviceTagsMap = new HashMap<>();
        Map<String, Map<String, Object>> mysqlDeviceFieldsMap = new HashMap<>();
        Map<String, Map<String, String>> mysqlDeviceTagsMap = new HashMap<>();
        //存储表名称
        Map<String, String> tableNameMap = new HashMap<>();

        data.forEach((key, body) -> {
            Body frameData = (Body) body;
            IotFuncDict funcDictMap = dictService.getMeasurementDictMap(frameData.getFuncCode());
            String deviceId =iotService.geSensorIdByRemoteIdANDFuncCode(frameData.getRemoteId(),frameData.getFuncCode());

            if(ObjectUtil.isEmpty(funcDictMap) || ObjectUtil.isEmpty(deviceId)) {
                return ;
            }


            String remoteId = frameData.getRemoteId();

            // 初始化或获取该设备的 fields 和 tags
            deviceFieldsMap.putIfAbsent(deviceId, new HashMap<>());
            deviceTagsMap.putIfAbsent(deviceId, new HashMap<>());
            mysqlDeviceFieldsMap.putIfAbsent(deviceId, new HashMap<>());
            mysqlDeviceTagsMap.putIfAbsent(deviceId, new HashMap<>());

            Map<String, Object> fields = deviceFieldsMap.get(deviceId);
            Map<String, String> tags = deviceTagsMap.get(deviceId);
            Map<String, Object> mysqlFields = mysqlDeviceFieldsMap.get(deviceId);
            Map<String, String> mysqlTags = mysqlDeviceTagsMap.get(deviceId);

            // 设置 tags（只需设置一次）
            if (!tags.containsKey("deviceId")) {
                //插入表名称
                tableNameMap.put(deviceId+"mysql", funcDictMap.getTableName());
                tableNameMap.put(deviceId+"influxdb", funcDictMap.getMeasurement());
                tags.put("deviceId", deviceId);
                tags.put("remoteId", remoteId);
                mysqlTags.put("device_id", deviceId);
                mysqlTags.put("remote_id", remoteId);

//                String reportTime = TimeUtils.formatStandard(frameData.getReportTime()); // 转换为 "yyyy-MM-dd HH:mm:ss"
                String reportTime = TimeUtils.formatStandardWithOneHourAdded(frameData.getReportTime());
                fields.put("reportTime", reportTime);
                fields.put("sendTime", frameData.getSendTime());
                fields.put("reportText", frameData.getOriginalCode());
//                fields.put("funcCode", frameData.getFuncCode());
                mysqlFields.put("report_time", reportTime);
                mysqlFields.put("update_time", frameData.getSendTime());
                mysqlFields.put("report_text", frameData.getOriginalCode());
//                mysqlFields.put("func_code", frameData.getFuncCode());
            }

            fields.put(funcDictMap.getFieldName(), frameData.getFormatValue());

            mysqlFields.put(funcDictMap.getMysqlFieldName(), frameData.getFormatValue());
        });
        // 遍历分组后的数据，统一写入 InfluxDB / MySQL
        deviceFieldsMap.forEach((item,dataFields) -> {
            Map<String, Object> mysqlDataFields = mysqlDeviceFieldsMap.get(item);
            if (item.equals("SWJ-A001"))
            {
                Object current = dataFields.get("valueCurrent");
                String capacity = ReservoirCapacityCalculator.linearInterpolation2(current);
                dataFields.put("reservoirCapacity",capacity);
                mysqlDataFields.put("reservoir_capacity",capacity);
            }else if (item.contains("FSFXY-")){
                String valueFx = dataFields.get("valueFx").toString();
                String fx = WindDirectionUtil.degreeToDirection(valueFx);
                dataFields.put("valueFx", fx);
                mysqlDataFields.put("value_fx",fx);
            }
            Map<String, String> tagParams = deviceTagsMap.get(item);
            iotService.saveToInfluxDB(tableNameMap.get(item+"influxdb"), tagParams, dataFields);
            boolean sameHour = TimeUtils.isSameHour((String) mysqlDataFields.get("update_time"), (String) mysqlDataFields.get("report_time"));
            if (sameHour){
                iotService.saveToMysql(tableNameMap.get(item+"mysql"), mysqlDeviceTagsMap.get(item), mysqlDataFields);
            }
        });
    }


    private IotSensorMsg saveSensorMsg(FrameReqWrapper frameReqWrapper) {
        IotSensorMsg build = IotSensorMsg.builder()
                .funcCode(frameReqWrapper.getReqDto().getHeader().getFuncCode())
                .remoteId(frameReqWrapper.getReqDto().getHeader().getRemoteStationAddr())
                .reportTime(TimeUtils.formatStandardWithOneHourAdded(frameReqWrapper.getReqDto().getBody().getObserveTime()))
                .sendTime(frameReqWrapper.getReqDto().getBody().getSendTime())
                .msg(frameReqWrapper.getOriginalFrame())
                .build();
        iotService.saveSensorMsg(build);
        return build;
    }



    /**
     * 对两个表示 double 值的字符串进行相加
     *
     * @param str1 第一个字符串形式的 double 值
     * @param str2 第二个字符串形式的 double 值
     * @return 相加后的字符串结果
     * @throws NumberFormatException 如果任一输入无法解析为 double
     */
    public String addStringDoubles(String str1, String str2) throws NumberFormatException {
        if (str1 == null || str2 == null) {
            throw new IllegalArgumentException("Input strings cannot be null");
        }
        if (str2.equals("null")){
            return str1;
        }

        try {
            double d1 = Double.parseDouble(str1.trim());
            double d2 = Double.parseDouble(str2.trim());
            double sum = d1 + d2;
            return String.valueOf(sum);
        } catch (NumberFormatException e) {
            throw new NumberFormatException("Invalid double format in input strings: " + e.getMessage());
        }
    }


}
